<?php
/*
    Dill is a tool for creating websites with PHP.
    Copyright (C) 2014  Jannik Haberbosch

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


function rmtree( $dir )
{
	/* On success, removes an entire directory tree and returns 'TRUE'.
	Otherwise 'FALSE' is returned.
	'$dir' needs to be an absolute path to the directory which should be
	emptied.
	Info: The directory specified by '$dir' will be removed aswell.

	On failure, FALSE is returned.

	Parameters		$dir
					string
					The absolute path to the directory which should be removed.

	Return values	TRUE/FALSE
					TRUE is returned in case of success; FALSE on failure.
	
	*/
	if( !file_exists( $dir ) ) return FALSE;
	if( !is_dir( $dir ) ) return FALSE;

	foreach( scandir( $dir ) as $element )
	{
		if( in_array( $element, array( ".", ".." ) ) ) continue;

		if( is_dir( makepath( $dir, $element ) ) )
		{
			if( count( scandir( makepath( $dir, $element ) ) ) == 2 )
			{
				// The directory is empty. It can be deleted.
				if( !rmdir( makepath( $dir, $element ) ) )
				{
					return FALSE;
				}
				else
				{
					continue;
				}
			}
			else
			{
				// The directory is not empty.
				if( !rmtree( makepath( $dir, $element ) ) )
				{
					return FALSE;
				}
				else
				{
					// The directory is empty now.
					if( !rmdir( makepath( $dir, $element ) ) )
					{
						return FALSE;
					}
					else
					{
						continue;
					}
				}
			}
		}
		else
		{
			// Delete the file.
			if( !unlink( makepath( $dir, $element ) ) )
			{
				return FALSE;
			}
			else
			{
				continue;
			}
		}
	}

	// At last remove the top-level directory itself.
	$debug_backtrace = debug_backtrace();
	if( strcmp( $debug_backtrace[1]["function"], __FUNCTION__ ) != 0 )
	{
		if( !rmdir( $dir ) )
		{
			return FALSE;
		}
	}
	return TRUE;
}


function copytree( $src, $dest )
{
	/* Copies an entire directory tree from source to destination.
	'$src' and '$dest' need to be absolute paths.

	Returns 'TRUE' on success, otherwise 'FALSE'.

	Parameters		$src
					string
					Absolute path to the source directory.

					$dst
					string
					Absolute path to the destination directory.

	Return values	TRUE/FALSE
					Returns FALSE on failure; TRUE on success.
	
	*/
	if( !file_exists( $src ) || file_exists( $dest ) ) return FALSE;
	if( !is_dir( $src ) ) return FALSE;

	// Attempt to create the destination directory.
	if( !mkdir( $dest, 0777, TRUE ) ) return FALSE;

	foreach( scandir( $src ) as $src_element )
	{
		if( in_array( $src_element, array( ".", ".." ) ) ) continue;
		
		if( is_dir( makepath( $src, $src_element ) ) )
		{
			if( !copytree( makepath( $src, $src_element ), makepath( $dest, $src_element ) ) )
			{
				return FALSE;
			}
			else
			{
				continue;
			}
			
		}
		else
		{
			if( !file_exists( makepath( $dest, $src_element ) ) )
			{
				if( !copy( makepath( $src, $src_element ), makepath( $dest, $src_element ) ) )
				{
					return FALSE;
				}
				else
				{
					continue;
				}
			}
			else
			{
				return FALSE;
			}
		}
	}
	return TRUE;
}


function dirwalk( $abspath, $arr )
{
	/* Walks through an entire directory tree and returns an
	array that consists of the many arrays that represents the directory tree
	structure.

	Parameters		$abspath
					string
					The absolute path to a directory.

					$arr
					array
					An array that contains the exact representation of a directory
					structure.

	Return values	Array
					An array that contains the exact represenation of a
					directory structure.

	Note:  This function calls itself recursively.
	*/

	$dirs_files = scandir( $abspath );
	$dirs_files = array_diff( $dirs_files, array( ".", ".." ) );

	$dirs = array_filter( $dirs_files, function( $item ) use ( $abspath )
	{
		return is_dir( makepath( $abspath, $item ) );
	});

	$files = array_diff( $dirs_files, $dirs );


	foreach( $dirs as $d )
	{
		$arr[$d] = dirwalk( makepath( $abspath, $d ), array() );
	}

	foreach( $files as $f )
	{
		$arr[] = $f;
	}

	return $arr;
	
}


function get_dirnames( $abs_dir_path )
{
	/* Returns the names of all directories inside a directory specified by
	'$abs_dir_path'.  The directories "." and ".." are removed before the array
	is returned.
	In case of an error, a boolean FALSE is returned.

	Parameters		$abs_dir_path
					string
					Defines the absolute path to a directory.
		
	Return value	Array or FALSE
					FALSE is returned in case of an error.
		
	*/
	if( !file_exists( $abs_dir_path ) ) return FALSE;
	if( !is_dir( $abs_dir_path ) ) return FALSE;


	$all_dirs = array_filter( scandir( $abs_dir_path ), function( $element ) use( $abs_dir_path )
	{
		return is_dir( makepath( $abs_dir_path, $element ) );
	});	


	$all_dirs = array_remove( $all_dirs, "." );
	$all_dirs = array_remove( $all_dirs, ".." );	
	
	return $all_dirs;
}


function get_filenames( $abs_dir_path, $no_extension = FALSE )
{
	/* Returns all filenames of files inside a directory specified by
	'$abs_dir_path'.  If '$no_extension' is set to 'TRUE', then all file
	extensions of all files found are removed before the array is returned.
	By default, extensions are not removed.
	On failure, FALSE is returned.

	Parameters		$abs_dir_path
					string
					Defines the absolute path to a directory.
			
					$no_extension
					boolean
					Controls whether the extenions of all files
					should be removed or not.  If removal is required, this
					should be set to 'TRUE'.

	Return values	array
					Contains the filenames of all files that have been found in
					the directory pointed to by '$abs_dir_path'.

	*/
	if( !file_exists( $abs_dir_path ) ) return FALSE;
	if( !is_dir( $abs_dir_path ) ) return FALSE;
	if( !is_bool( $no_extension ) ) return FALSE;

	// Store all filenames in a new array.
	$files = array_filter( scandir( $abs_dir_path ), function( $element ) use( $abs_dir_path )
	{
		return is_file( makepath( $abs_dir_path, $element ) );
	});

	// Optionally, remove file extensions.
	if( $no_extension )
	{
		$files = array_map( function( $element )
		{
			$path_parts = pathinfo( $element );
			return $path_parts["filename"];
		}, $files );
	}

	return $files;
}


function write_files( $filesdata )
{
	/* Appends a string to a file that already exists.
	On Success, 'TRUE' is returned, otherwise 'FALSE'.

	Parameters		$filesdata
					array of arrays
					This is an array that should consist of several arrays.
					Each of these arrays should be of the length two.  The first
					element inside such an array is the absolute path to the
					file which should be appended to.  The second element is
					string (content) that should be written to this file.

	Return values	Boolean
					On success, 'TRUE' is returned, otherwise 'FALSE'.
		
	*/
	foreach( $filesdata as $filedata )
	{
		if( file_exists( $filedata[0] ) && is_writeable( $filedata[0] ) &&
			filesize( $filedata[0] ) == 0 )
		{
			$fh = fopen( $filedata[0], "a" );
			if( $fh == FALSE )
			{
				log_to_console( sprintf( MSG_FILE_NOT_OPENED, $filedata[0] ) );
				return FALSE;
			}
			if( fwrite( $fh, $filedata[1] ) == FALSE )
			{
				log_to_console( sprintf( MSG_FILE_WRITING_ERROR, $filedata[0] ) );
				fclose( $fh );
				return FALSE;
			}
			fclose( $fh );
			log_to_console( sprintf( MSG_FILE_WRITING_OK, $filedata[0] ) );
		}
	}
	return TRUE;
}


function create_files( $files )
{
	/* Creates all files defined in the array "$files".  Each of these
	elements is a "String", and needs to be an absolute path.
	On failure, FALSE is returned.

	Parameters		$files
					Array containing none, one or several strings.

	Return value	Boolean
					Returns 'TRUE' on success otherwise 'FALSE'.
		
	*/
	foreach( $files as $file )
	{
		if( file_exists( $file ) )
		{
			log_to_console( sprintf( MSG_FILE_ALREADY_EXISTS, $file ) );
			continue;
		}
		else
		{
			$fh = fopen( $file, "w" );
			if( $fh == FALSE )
			{
				log_to_console( sprintf( MSG_FILE_NO_CREATION, $file ) );
				return FALSE;
			}	
			fclose( $fh );
			log_to_console( sprintf( MSG_FILE_CREATED, $file ) );
		}
	}
	return TRUE;
}


function create_directories( $directories, $mode = 0777, $create_subdirs = FALSE )
{
	/* Creates many directories on the current file system, including sub-
	directories if wanted.  If all went well, 'TRUE' is returned; otherwise
	'FALSE' is returned.

	Parameters		$directories
					array of strings
					An array containing one or more directories to create.
		
					$mode
					char
					The mode which should be applied to each directory when
					creating it.
					This option is ignored on Windows, but has to be passed
					nontheless.
		
					$create_subdirs
					boolean
					If set to 'TRUE', intermediate directories will be created
					aswell.

	Return values	Boolean
					'TRUE' on success, 'FALSE' on failure.
			
	*/
	foreach( $directories as $dir )
	{
		if( file_exists( $dir ) )
		{
			log_to_console( sprintf( MSG_DIR_ALREADY_EXISTS, $dir ) );
			continue;
		}
		if( !mkdir( $dir, $mode, $create_subdirs ) )
		{
			log_to_console( sprintf( MSG_DIR_NO_CREATION, $dir ) );
			return FALSE;
		}
		log_to_console( sprintf( MSG_DIR_CREATED, $dir ) );
	}
	return TRUE;
}


function filter_dirpaths( $haystack, $needles )
{
	/* Returns a new array that contains only absolute paths to
	directories.

	Parameters		$haystack
					array
					Absolute paths to directories and/or files.

					$needles
					array
					Several file endings by which all paths will either be filtered
					out or not.

	Return values	Array
					Contains the absolute paths to each directory.
	*/
	return array_diff( $haystack, filter_filepaths( $haystack, $needles ) );
}


function filter_filepaths( $haystack, $needles )
{
	/* Returns an array that consists of absolute file paths that point to a
	file.

	Parameters		$haystack
					array
					Absolute paths to directories and/or files.

					$needles
					Several file endings by which all paths will be either
					filtered out or not.

	Return values	Array
					Contains the absolute paths to each file.

	*/
	$filepaths = array_filter( $haystack, function( $path ) use ( $needles )
	{
		foreach( $needles as $file_ending )
		{
			if( endswith( $path, $file_ending ) )
			{
				return TRUE;
			}
		}
		return FALSE;
	});

	return $filepaths;
}


function makepath()
{
	/* Returns a path by joining path components together.
	Platform independent.

	Parameters		Receives n arguments.
					Each parameter should be of type "string".

	Return values	string
					Returns the new constructed filepath.

	*/
	return join( DIRECTORY_SEPARATOR, func_get_args() );
}

?>
